<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>使用纹理贴图的几何体，设置纹理UV坐标</title>
    <style>
      body {
        margin: 0;
        overflow: hidden;
        /* 隐藏body窗口区域滚动条 */
      }
    </style>
    <!--引入three.js三维引擎-->
    <!-- <script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script> -->
    <!-- <script src="http://www.yanhuangxueyuan.com/threejs/build/three.js"></script> -->
    <!-- Three.js-master 包示例中的用法 examples/js/controls/OrbitControls.js  版本要匹配-->
    <script src="http://www.yanhuangxueyuan.com/3D/example/three.min.js"></script>
    <script src="../../util/OrbitControls.js"></script>
  </head>
  <body>
    <script type="module">
      import {
        RAFTime,
        sphereMesh,
        cylinderMesh,
        v2ToV3,
      } from "../../util/index.js";

      /**
       * 创建场景对象Scene
       */
      var scene = new THREE.Scene();

      /**
       * 辅助坐标系 参数250表示坐标系大小，可以根据场景大小去设置
       */
      var axisHelper = new THREE.AxisHelper(1000);
      scene.add(axisHelper);

      //矩形平面，细分数默认1，即2个三角形拼接成一个矩形--------------------顶点数据，使用同一个点的纹理UV坐标，取相同的RGB值
      // var geometry = new THREE.PlaneGeometry(204, 102);

      // var ImageLoader = new THREE.ImageLoader();
      // // load方法回调函数，按照路径加载图片，返回一个html的元素img对象
      // ImageLoader.load("../../public/images/Earth.png", function (img) {
      //   // image对象作为参数，创建一个纹理对象Texture
      //   var texture = new THREE.Texture(img);
      //   // 下次使用纹理时触发更新
      //   texture.needsUpdate = true;
      //   var material = new THREE.MeshStandardMaterial({
      //     map: texture, //设置纹理贴图
      //   });

      //   console.log("geometry.faceVertexUvs---", geometry.faceVertexUvs);

      //   /**
      //    * 遍历uv坐标，faceVertexUvs几何体面的顶点纹理坐标
      //    */
      //   geometry.faceVertexUvs[0].forEach((elem) => {
      //     elem.forEach((Vector2) => {
      //       // 所有顶点的纹理UV坐标全部设置为一个值，则此纹理UV坐标点上的RGB值用来渲染整个几何体
      //       Vector2.set(0.6, 0.6);
      //     });
      //   });
      //   var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
      //   scene.add(mesh); //网格模型添加到场景中
      // });

      // 矩形平面 设置细分数4,4-16个矩形 => 32个三角形组成----------------------------------------将其中的某个三角形,映射为完整的纹理贴图
      // var geometry = new THREE.PlaneGeometry(204, 102, 4, 4);

      // var textureLoader = new THREE.TextureLoader();
      // // 执行load方法，加载纹理贴图成功后，返回一个纹理对象Texture
      // textureLoader.load("../../public/images/Earth.png", function (texture) {
      //   /**
      //    * 局部三角面显示完整纹理贴图
      //    */
      //   var t0 = new THREE.Vector2(0, 1); //图片左上角
      //   var t1 = new THREE.Vector2(0, 0); //图片左下角
      //   var t2 = new THREE.Vector2(1, 0); //图片右下角
      //   var t3 = new THREE.Vector2(1, 1); //图片右上角
      //   var uv1 = [t0, t1, t3]; //选中图片的左上角三角形
      //   var uv2 = [t1, t2, t3]; //选中图片的右下角三角形
      //   // 设置第五、第六个三角形面对应的纹理坐标------它只有一个面32个三角形
      //   geometry.faceVertexUvs[0][4] = uv1;
      //   geometry.faceVertexUvs[0][5] = uv2;
      //   // // 第6个三角形,全取一个值的情况
      //   // geometry.faceVertexUvs[0][5] = new THREE.Vector2(1, 1);

      //   console.log("geometry.faceVertexUvs---", geometry.faceVertexUvs);

      //   var material = new THREE.MeshLambertMaterial({
      //     // color: 0x0000ff,// 设置颜色纹理贴图：Texture对象作为材质map属性的属性值
      //     side: THREE.DoubleSide, //两面可见
      //     map: texture, //设置颜色贴图属性值
      //   }); //材质对象Material

      //   var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
      //   scene.add(mesh); //网格模型添加到场景中
      // });

      // BufferGeometry自定义一个细分度为1的,2个三角形的几何体----------------------------------类似于第一个例子,映射了完整贴图纹理
      var geometry = new THREE.BufferGeometry(); //创建一个空几何体对象

      var textureLoader = new THREE.TextureLoader();
      // 执行load方法，加载纹理贴图成功后，返回一个纹理对象Texture
      textureLoader.load("../../public/images/Earth.png", function (texture) {
        //类型数组创建顶点位置position数据
        var vertices = new Float32Array([
          0,
          0,
          0, //顶点1坐标
          80,
          0,
          0, //顶点2坐标
          80,
          80,
          0, //顶点3坐标
          0,
          80,
          0, //顶点4坐标
        ]);
        // 创建属性缓冲区对象
        var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组
        // 设置几何体attributes属性的位置position属性
        geometry.attributes.position = attribue;
        var normals = new Float32Array([
          0,
          0,
          1, //顶点1法向量
          0,
          0,
          1, //顶点2法向量
          0,
          0,
          1, //顶点3法向量
          0,
          0,
          1, //顶点4法向量
        ]);
        // 设置几何体attributes属性的位置normal属性
        geometry.attributes.normal = new THREE.BufferAttribute(normals, 3); //3个为一组,表示一个顶点的xyz坐标
        // Uint16Array类型数组创建顶点索引数据
        var indexes = new Uint16Array([0, 1, 2, 0, 2, 3]);
        // 索引数据赋值给几何体的index属性
        geometry.index = new THREE.BufferAttribute(indexes, 1); //1个为一组
        /**纹理坐标*/
        var uvs = new Float32Array([
          0,
          0, //图片左下角
          1,
          0, //图片右下角
          1,
          1, //图片右上角
          0,
          1, //图片左上角
        ]);
        // 设置几何体attributes属性的位置normal属性
        geometry.attributes.uv = new THREE.BufferAttribute(uvs, 2); //2个为一组,表示一个顶点的纹理坐标

        var material = new THREE.MeshLambertMaterial({
          // color: 0x0000ff,// 设置颜色纹理贴图：Texture对象作为材质map属性的属性值
          side: THREE.DoubleSide, //两面可见
          map: texture, //设置颜色贴图属性值
        }); //材质对象Material

        var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
        scene.add(mesh); //网格模型添加到场景中
      });

      // // Geometry自定义一个细分度为1的,2个三角形的几何体----------------------------------类似于第一个例子,映射了完整贴图纹理
      // var geometry = new THREE.Geometry(); //创建一个空几何体对象

      // var textureLoader = new THREE.TextureLoader();
      // // 执行load方法，加载纹理贴图成功后，返回一个纹理对象Texture
      // textureLoader.load("../../public/images/Earth.png", function (texture) {
      //   /**顶点坐标(纹理映射位置)*/
      //   var p1 = new THREE.Vector3(0, 0, 0); //顶点1坐标
      //   var p2 = new THREE.Vector3(160, 0, 0); //顶点2坐标
      //   var p3 = new THREE.Vector3(160, 80, 0); //顶点3坐标
      //   var p4 = new THREE.Vector3(0, 80, 0); //顶点4坐标
      //   geometry.vertices.push(p1, p2, p3, p4); //顶点坐标添加到geometry对象
      //   /** 三角面1、三角面2*/
      //   var normal = new THREE.Vector3(0, 0, 1); //三角面法向量
      //   var face0 = new THREE.Face3(0, 1, 2, normal); //三角面1
      //   var face1 = new THREE.Face3(0, 2, 3, normal); //三角面2
      //   geometry.faces.push(face0, face1); //三角面1、2添加到几何体

      //   /**纹理坐标*/
      //   var t0 = new THREE.Vector2(0, 0); //图片左下角
      //   var t1 = new THREE.Vector2(1, 0); //图片右下角
      //   var t2 = new THREE.Vector2(1, 1); //图片右上角
      //   var t3 = new THREE.Vector2(0, 1); //图片左上角
      //   var uv1 = [t0, t1, t2]; //选中图片一个三角区域像素——映射到三角面1
      //   var uv2 = [t0, t2, t3]; //选中图片一个三角区域像素——映射到三角面2
      //   geometry.faceVertexUvs[0].push(uv1, uv2); //纹理坐标传递给纹理三角面属性

      //   console.log("geometry.faceVertexUvs---", geometry.faceVertexUvs);

      //   var material = new THREE.MeshLambertMaterial({
      //     // color: 0x0000ff,// 设置颜色纹理贴图：Texture对象作为材质map属性的属性值
      //     side: THREE.DoubleSide, //两面可见
      //     map: texture, //设置颜色贴图属性值
      //   }); //材质对象Material

      //   var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
      //   scene.add(mesh); //网格模型添加到场景中
      // });

      // 平行光
      var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
      // 平行光辅助对象
      const directionalLightHelper = new THREE.DirectionalLightHelper(
        directionalLight,
        10
      );
      scene.add(directionalLightHelper);
      // 设置光源位置
      directionalLight.position.set(45, 75, 30);
      scene.add(directionalLight);
      // 设置用于计算阴影的光源对象
      directionalLight.castShadow = true;
      // 设置计算阴影的区域，最好刚好紧密包围在对象周围
      // 计算阴影的区域过大：模糊  过小：看不到或显示不完整
      directionalLight.shadow.camera.near = 0.5;
      directionalLight.shadow.camera.far = 300;
      directionalLight.shadow.camera.left = -100;
      directionalLight.shadow.camera.right = 100;
      directionalLight.shadow.camera.top = 200;
      directionalLight.shadow.camera.bottom = -200;
      // 设置mapSize属性可以使阴影更清晰，不那么模糊
      directionalLight.shadow.mapSize.set(1024, 1024);
      console.log(directionalLight.shadow.camera);

      //环境光
      var ambient = new THREE.AmbientLight(0x000000);
      scene.add(ambient);

      /**
       * 相机设置
       */
      var width = window.innerWidth; //窗口宽度
      var height = window.innerHeight; //窗口高度
      var k = width / height; //窗口宽高比
      var s = 100; //三维场景显示范围控制系数，系数越大，显示的范围越大
      //创建相机对象
      var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
      camera.position.set(60, 60, 60); //设置相机位置
      camera.lookAt(scene.position); //设置相机方向(指向的场景对象)

      /**
       * 创建渲染器对象
       */
      var renderer = new THREE.WebGLRenderer();
      renderer.setSize(width, height); //设置渲染区域尺寸
      renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
      // 设置渲染器，允许光源阴影渲染
      renderer.shadowMap.enabled = true;
      document.body.appendChild(renderer.domElement); //body元素中插入canvas对象

      // // 监听鼠标、键盘事件，实现 旋转/缩放/平移
      // function render() {
      //   renderer.render(scene, camera); //执行渲染操作
      // }

      // 递归函数,每1/60秒左右,调用一次自己
      const render = RAFTime((t) => {
        renderer.render(scene, camera); //执行渲染操作
        // line.rotateY(0.001 * t);
      });

      // 启动渲染
      render();

      // 创建，轨迹控件
      var controls = new THREE.OrbitControls(camera, renderer.domElement);
      // 监听鼠标、键盘事件
      controls.addEventListener("change", render);
    </script>
  </body>
</html>
